/*
 * Copyright 2007-2010 Jeremy Brown.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author <a href="http://jeremy-brown.com">Jeremy Brown</a>
 */


package groovy.prefuse.factory

import prefuse.Visualization
import prefuse.data.Table
import prefuse.data.Graph
import prefuse.data.io.*

public class TableFactory extends AbstractFactory
{
	boolean isLeaf()
	{
		return false
	}
    
    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException
	{
		def file = attributes.remove("file")
		def url = attributes.remove("url")
		def csv = attributes.remove("csv")
		def delimiter = attributes.remove("\t")
		def hasHeader = attributes.remove("hasHeader")
		def columnNames = attributes.remove("columnNames")
		def columnWidths = attributes.remove("columnWidths")
		def columnTypes = attributes.remove("columnTypes")
		def columnDefaults = attributes.remove("columnDefaults")

		builder.context.group = builder.pickPrefuseGroup(attributes)   //saved and used only in the case of pfVisualization(){table()}
		builder.context.predicate = attributes.remove("predicate")	//saved and used only in the case of pfVisualization(){table()}
		builder.context.visualID = attributes.remove("visualID") //saved and used as the id to save the VisualTable as

		def tableType = attributes.tableType
		if(attributes.tableType && (attributes.tableType.toLowerCase =~ /edge|edges|node|nodes/))
		{
			builder.context.tableType = attributes.remove("tableType").toLowerCase() //saved and used only in the case of pfGraph(){pfTable()} or pfTree(){pfTable()} - valid options are "edge" and "node"
		}
		
		Table table
				
		//If a Table object is passed in as the value, simply return it
		if(value instanceof Table)
		{
			table = value
		}
		else if(attributes.table instanceof Table)
		{
			table = attributes.remove("table")
		}
		
		if(table)
		{
			changeColumnNames(table, columnNames)
			return table
		}
		


		//Otherwise we create a Table (by reading it from a file/url, or by creating an empty Table)
		
		//File/url name can be passed in as the file:, url:, or csv: attributes
		if(file == null && url != null)
		{
			file = url
			url = null
		}
		else if(file == null && (csv instanceof String || csv instanceof File || csv instanceof URL))
		{
			file = csv
			csv = true
		}
		
		hasHeader = hasHeader ? true : false
		

		//Create Table by reading a file
		if(file != null)
		{
			def tableReader
			//Fixed width files identified by presence of columnWidths attribute
			if(columnWidths != null)
			{
				def schema
				if(columnNames != null && columnNames.size() == columnWidths.size())
				{
					schema = FixedWidthTextTableSchemaFactory.newInstance(columnNames, columnWidths)
				}
				else
				{
					schema = FixedWidthTextTableSchemaFactory.newInstance(columnWidths)
				}
				tableReader = new FixedWidthTextTableReader(schema)
			}
			//CSV files identified by csv:true (or csv:my/filename/here.txt)
			else if(csv)
			{
				tableReader = new CSVTableReader()
			}
			//Files where columns are delimited by a set of characters, specified with a regular expression in the delimiter: attribute
			else
			{
				if(delimiter == null)
				{
					delimiter = "\t"
				}
				tableReader = new DelimitedTextTableReader(delimiter)
			}
			
			//Read the table data in from the file/url
			tableReader.setHasHeader(hasHeader)
			table = tableReader.readTable(file)
		}
		
		//Create an empty Table (maybe with column names)
		else
		{
			table = new Table()
			if(columnNames instanceof List && columnTypes instanceof List && (columnNames.size() == columnTypes.size()))
			{
				columnNames.eachWithIndex{columnName, index->
					if(columnDefaults instanceof List && columnDefaults.size() == columnNames.size())
					{
						table.addColumn(columnName, columnTypes[index], columnDefaults[index])
					}
					else
					{
						table.addColumn(columnName, columnTypes[index])
					}
				}
			}
		}
		
		changeColumnNames(table, columnNames)

		return table
    }
	
	
	public void changeColumnNames(Table table, List names)
	{
		if(names && names instanceof List)
		{
			names.eachWithIndex{name, index->
				def oldName = table.getColumnName(index)
				if(oldName != name)
				{
					table.m_entries[name] = table.m_entries[oldName]
					table.m_entries.remove(oldName)
					table.m_names[index] = name
				}
			}
		}
	}
	
	
	public void setParent(FactoryBuilderSupport builder, Object parent, Object child)
	{
		if(parent instanceof Visualization)
		{
			def visualTable
			if(builder.context.group && builder.context.predicate)
			{
				visualTable = parent.addTable(builder.context.group, child, builder.context.predicate)
			}
			else if(builder.context.group)
			{
				visualTable = parent.addTable(builder.context.group, child)
			}
			
			//Store the visualTable in the specified visualID (if any)
			if(builder.context.visualID)
			{
				builder.setVariable(builder.context.visualID, visualTable)
			}
		}
		else if(parent instanceof Graph)
		{
			if(builder.context.tableType =~ /edge|edges/)
			{
				parent.setEdgeTable(child)
			}
			else if(builder.context.tableType =~ /node|nodes/)
			{
				parent.setNodeTable(child)
			}
		}
	}
}
